using System;
using System.Diagnostics.Contracts;
using Microsoft.Practices.ObjectBuilder2;
using Microsoft.Practices.Unity;

namespace Sessionizer.Core.Unity
{
    /// <summary>
    ///     Lifetime manager for ISession.
    ///     Stores given component instance inside a current session's storage.
    ///     Throws if there is no currently bound session;
    /// </summary>
    public sealed class SessionLifetimeManager : SynchronizedLifetimeManager
    {
        private readonly object _syncRoot;
        private volatile NamedTypeBuildKey _buildKey;

        public SessionLifetimeManager()
        {
            _buildKey = null;
            _syncRoot = new object();
        }

        public void SetBuildKey(NamedTypeBuildKey buildKey)
        {
            Contract.Requires<ArgumentNullException>(buildKey != null);
            if (ReferenceEquals(_buildKey, null))
            {
                lock (_syncRoot)
                {
                    if (ReferenceEquals(_buildKey, null))
                    {
                        _buildKey = buildKey;
                        return;
                    }
                }
            }
            if (!Equals(_buildKey, buildKey))
            {
                throw new InvalidOperationException("Cannot change buildKey associated with instance");
            }
        }

        protected override object SynchronizedGetValue()
        {
            if (ReferenceEquals(_buildKey, null))
            {
                lock (_syncRoot)
                {
                    if (ReferenceEquals(_buildKey, null))
                    {
                        return null;
                    }
                }
            }
            return Session.Current.Storage.Get(_buildKey);
        }


        protected override void SynchronizedSetValue(object newValue)
        {
            if (ReferenceEquals(_buildKey, null))
            {
                lock (_syncRoot)
                {
                    if (ReferenceEquals(_buildKey, null))
                    {
                        throw new InvalidOperationException(
                            "BuildKey is not set");
                    }
                }
            }

            Session.Current.Storage.Set(_buildKey, newValue);
        }

        public override void RemoveValue()
        {
        }
    }
}